1 Investigating MLL samples

2 Transcriptional profile PCA and differential gene expression (DGA)

2.1 Sorting

Isolated cells were sorted using a variety of cell markers

2.2 RNA-seq

RNA was isolated and sequenced from the various cell types.

2.3 Sample summary

sample_table <- read.delim('sample.table', stringsAsFactors = FALSE)

rownames(sample_table) <- sample_table$Nextera_XT_index

sample_table$Nextera_XT_index <- NULL
sample_table$Replicates. <- NULL
sample_table$deseq_groups <- c("1","6","3","2","4","1","3","2","5","1","5","2","5","1","3","6","2","6","3")
datatable(sample_table) %>% 
  formatStyle("Phenotype", backgroundColor = styleEqual(c("CD33+","CD33+GFP-","CD33+GFP+","CD19+","CD19+GFP+"), c("#EAD3BF","#AA9486", "#B6854D","#9986A5","#79402E")))

2.4 PCA

2.4.1 PCA working with FPKMS

# fpkm_data <- purrr::map(paste0("myriad/",rownames(sample_table),"/replicate_1/stringtie/",rownames(sample_table),".gene_abund.tab"), read.table, stringsAsFactors=FALSE, sep="\t", header = TRUE) %>% reduce(left_join, by = "Gene.ID") %>% select("Gene.ID","Gene.Name",contains("FPKM"))
# 
# colnames(fpkm_data) <- c("Gene.ID","Gene.Name",rownames(sample_table))
# saveRDS(fpkm_data,"fpkm_data.rds")
fpkm_data <- readRDS("fpkm_data.rds")
## TPM data
# tpm_data <- purrr::map(paste0("myriad/",rownames(sample_table),"/replicate_1/stringtie/",rownames(sample_table),".gene_abund.tab"), read.table, stringsAsFactors=FALSE, sep="\t", header = TRUE) %>% reduce(left_join, by = "Gene.ID") %>% select("Gene.ID","Gene.Name",contains("TPM"))
# 
# colnames(tpm_data) <- c("Gene.ID","Gene.Name",rownames(sample_table))
# saveRDS(tpm_data , "tpm_data.rds")

tpm_data <- readRDS("tpm_data.rds")

## Filter genes with a low expression throughout
# - select rows with max value >= 2
fpkm_data <- fpkm_data[apply(fpkm_data[,3:21], 1, max) >= 2,]

fpkms_counts_only <- fpkm_data[,3:21]
## Log transform the data
log.data = log2(fpkms_counts_only+1)

colnames(log.data) <- colnames(fpkm_data[,3:21])
rownames(log.data) <- make.names(fpkm_data$Gene.Name,unique = TRUE)
## Transpose the matrix back for PCA
t_log.data = t(log.data)

fit <- prcomp(t_log.data)

x="PC1"
y="PC2"
x.lab = paste(x, sprintf('(%0.1f%% explained var.)', 100 * fit$sdev[which(colnames(fit$x) == x)]^2/sum(fit$sdev^2)))
y.lab = paste(y, sprintf('(%0.1f%% explained var.)', 100 * fit$sdev[which(colnames(fit$x) == y)]^2/sum(fit$sdev^2)))
data <- data.frame(fit$x)
data<-rownames_to_column(data, var = "sample")
data<-left_join(data,rownames_to_column(sample_table, var = "sample_id"), by = c("sample" = "sample_id"))

ggplot(data,aes(PC1,PC2, colour = Phenotype )) + geom_point(size = 3 ) + 
  geom_label_repel(aes(label = Samples), size = 2,box.padding   = 0.1,point.padding = 0.1,
                   segment.color = 'grey50') + theme_minimal() + xlab(x.lab) + ylab(y.lab) +
   ggtitle(label = "FPKM PCA; no quantile normalization")

2.4.2 PCA and quantile normalization

## Quantile normalization
normalized.data = normalize.quantiles(as.matrix(log.data)) # A quantile normalization.

## Transpose the matrix back for PCA
normalized.data = t(normalized.data)

## re-attach rownames colnames
rownames(normalized.data) <- colnames(log.data)
colnames(normalized.data) <- rownames(log.data)

fit <- prcomp(normalized.data)

x.lab = paste(x, sprintf('(%0.1f%% explained var.)', 100 * fit$sdev[which(colnames(fit$x) == x)]^2/sum(fit$sdev^2)))
y.lab = paste(y, sprintf('(%0.1f%% explained var.)', 100 * fit$sdev[which(colnames(fit$x) == y)]^2/sum(fit$sdev^2)))
data <- data.frame(fit$x)
data<-rownames_to_column(data, var = "sample")
data<-left_join(data,rownames_to_column(sample_table, var = "sample_id"), by = c("sample" = "sample_id"))

ggplot(data,aes(PC1,PC2, colour = Phenotype )) + geom_point(size = 3 ) + 
  geom_label_repel(aes(label = Samples), size = 2,box.padding   = 0.1,point.padding = 0.1,
                   segment.color = 'grey50') + theme_minimal() + xlab(x.lab) + ylab(y.lab) +
  ggtitle(label = "FPKM PCA; with quantile normalization")

2.4.3 TPMs without normalization

## Filter genes with a low expression throughout
# - select rows with max value >= 2
tpm_data <- tpm_data[apply(tpm_data[,3:21], 1, max) >= 2,]

tpm_counts_only <- tpm_data[,3:21]
## Log transform the data
log.data = log2(tpm_counts_only+1)

colnames(log.data) <- colnames(tpm_data[,3:21])
rownames(log.data) <- make.names(tpm_data$Gene.Name,unique = TRUE)
## Transpose the matrix back for PCA
t_log.data = t(log.data)

fit <- prcomp(t_log.data)

x="PC1"
y="PC2"
x.lab = paste(x, sprintf('(%0.1f%% explained var.)', 100 * fit$sdev[which(colnames(fit$x) == x)]^2/sum(fit$sdev^2)))
y.lab = paste(y, sprintf('(%0.1f%% explained var.)', 100 * fit$sdev[which(colnames(fit$x) == y)]^2/sum(fit$sdev^2)))
data <- data.frame(fit$x)
data<-rownames_to_column(data, var = "sample")
data<-left_join(data,rownames_to_column(sample_table, var = "sample_id"), by = c("sample" = "sample_id"))

ggplot(data,aes(PC1,PC2, colour = Phenotype )) + geom_point(size = 3 ) + 
  geom_label_repel(aes(label = Samples), size = 2,box.padding   = 0.1,point.padding = 0.1,
                   segment.color = 'grey50') + theme_minimal() + xlab(x.lab) + ylab(y.lab) +
   ggtitle(label = "TPM PCA; no quantile normalization")

##PCA with counts ###Using scater normalization

# big_counts <- paste0("myriad/",rownames(sample_table),"/replicate_1/qc/",rownames(sample_table), "_QC.summary.txt/QC.geneCounts.formatted.for.DESeq.txt.gz") %>% map(gzfile) %>% map(read.table, stringsAsFactors = FALSE, sep = "\t")  %>% reduce(left_join, by = "V1") 
# 
# colnames(big_counts) <- c("ensgene", rownames(sample_table))
# rownames(big_counts) <- big_counts$ensgene
# big_counts$ensgene <- NULL
# big_counts <- big_counts[!is.na(rowSums(big_counts)),]
# saveRDS(big_counts,"big_counts.rds")
big_counts <- readRDS("big_counts.rds")
count_matrix <- as.matrix(big_counts)
colnames(count_matrix) <- rownames(sample_table)
rownames(count_matrix) <- rownames(big_counts)

sce <- SingleCellExperiment(list(counts = count_matrix),
                            colData = sample_table)
sce <- sce[rowSums(counts(sce)) >  0,]
sce <- calculateQCMetrics(sce)
## prepare total count and total features data

my_df <- data.frame("sample_id" = colnames(sce), "total_counts" = sce$total_counts,
                    "total_features" = sce$total_features_by_counts )
ggplot(my_df,aes(total_counts)) + geom_histogram(bins = 10) + 
  theme_minimal() + ggtitle("Histogram of total counts") + ylab("number of samples") + xlab("total counts")

ggplot(my_df,aes(total_features)) + geom_histogram(bins = 10) + 
  theme_minimal() + ggtitle("Histogram of total features") + ylab("number of samples") + xlab("total features")

###PCA without batch effect removal

sizeFactors(sce) <- librarySizeFactors(sce)
sce <- normalize(sce)

sce <- runPCA(sce)

data <- reducedDim(sce)
percent_var_PC1<- paste("PC1", sprintf('(%0.1f%% explained var.)', attr(data, 'percentVar')[1] * 100 ))
percent_var_PC2<- paste("PC2", sprintf('(%0.1f%% explained var.)', attr(data, 'percentVar')[2] * 100 ))

data <- data.frame(data)
data<-left_join(rownames_to_column(data), rownames_to_column(sample_table))

ggplot(data,aes(PC1,PC2,colour = Phenotype)) + geom_point(size = 3) + 
  geom_label_repel(label = data$Samples, size = 2,box.padding   = 0.1,point.padding = 0.1,
                   segment.color = 'grey50') + theme_minimal() + xlab(percent_var_PC1) + ylab(percent_var_PC2)

2.5 DEseq2

big_counts <- big_counts[-c(58736:58739),]

dds <- DESeqDataSetFromMatrix(big_counts, colData = sample_table, design = ~deseq_groups)
my_vst <- vst(dds)

pcaData <- DESeq2::plotPCA(my_vst, intgroup =c("Phenotype","Samples"),returnData = TRUE)
percentVar <- round(100 * attr(pcaData, "percentVar"))


ggplot(pcaData,aes(PC1,PC2,colour = Phenotype)) + geom_point(size = 3 ) +
        xlab(paste0("PC1: ",percentVar[1],"% variance")) +
        ylab(paste0("PC2: ",percentVar[2],"% variance")) + 
          geom_label_repel(aes(label = pcaData$Samples), size = 2,box.padding   = 0.1,point.padding = 0.1,
                   segment.color = 'grey50') +
        theme_minimal()

2.5.1 Genes driving PC1 and PC2

top_contribs = function(object) {
  # calculate the variance for each gene
  rv <- rowVars(assay(object))

  # select the 1000 top genes by variance
  select <- order(rv, decreasing=TRUE)[seq_len(min(1000, length(rv)))]

  # perform a PCA on the data in assay(x) for the selected genes
  pca <- prcomp(t(assay(object)[select,]))

  # the contribution to the total variance for each component
  percentVar <- pca$sdev^2 / sum( pca$sdev^2 )

  # Top 20 contributers to PC1 PC2
  PCA1_contrib <- sort(abs(pca$rotation[,1]), decreasing = TRUE )[1:20]
  PCA2_contrib <- sort(abs(pca$rotation[,2]), decreasing = TRUE )[1:20]
  PCA_contrib <- c(PCA1_contrib, PCA2_contrib)
  PCA_contrib <- data.frame("ensgene" = names(PCA_contrib), PCA_contrib)



  PCA_contrib <- cbind(PCA_contrib, data.frame("PC" = c(rep("PC1",20),rep("PC2","20"))))
  PCA_contrib <- left_join(PCA_contrib, grch38, by = "ensgene") %>% dplyr::select("PC","symbol", "biotype")
  return(PCA_contrib)
}

datatable(top_contribs(my_vst))

2.6 Differential Gene Expression

2.6.1 CD33+ vs CD33+GFP+

dds$deseq_groups <- relevel(dds$deseq_groups,ref = "2")

deseq <- DESeq(dds)

res <- results(deseq,contrast = c("deseq_groups", "1", "2"))
resOrdered <- res[order(res$padj),]
resOrdered <- resOrdered[complete.cases(resOrdered),]
resOrdered <- data.frame(resOrdered)
resOrdered <- left_join(rownames_to_column(resOrdered,var = "ensgene"), grch38, by = "ensgene") %>% select(symbol,log2FoldChange,padj)

res_for_table <- resOrdered  %>% dplyr::filter(padj < 0.1)
datatable(res_for_table, extensions = 'Buttons', options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))

2.6.2 GSEA CD33+ vs CD33+GFP+

hallmark <- gmtPathways("~/genome_apps/GSEA/h.all.v6.2.symbols.gmt")

plot_gsea <- function(res){
res <- res[complete.cases(res),]
res$fcSign <- sign(res$log2FoldChange)
res$logP <- -log10(res$padj)
res$metric <- res$logP/res$fcSign
y<-res[,c("symbol", "metric")]
geneList <- y$metric
names(geneList) <- y$symbol
geneList <- geneList[order(geneList, decreasing = T)]
fgseaRes <- fgsea(hallmark, geneList, nperm=10000, maxSize = 500)
fgseaRes <- fgseaRes[fgseaRes$padj < 0.05,]
plotGseaTable(hallmark[fgseaRes$pathway], geneList, fgseaRes, gseaParam = 0.4)
}

plot_gsea(resOrdered)

2.6.3 DGE CD33+GFP- vs CD33+GFP+

res <- results(deseq,contrast = c("deseq_groups", "3", "2"))
resOrdered <- res[order(res$padj),]
resOrdered <- resOrdered[complete.cases(resOrdered),]
resOrdered <- data.frame(resOrdered)
resOrdered <- left_join(rownames_to_column(resOrdered,var = "ensgene"), grch38, by = "ensgene") %>% select(symbol,log2FoldChange,padj) 

res_for_table <- resOrdered %>% dplyr::filter(padj < 0.1)
datatable(res_for_table, extensions = 'Buttons', options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))

2.6.4 GSEA CD33+GFP- vs CD33+GFP+

plot_gsea(resOrdered)

2.6.5 DGE CD19+ vs CD19+GFP+

dds$deseq_groups <- relevel(dds$deseq_groups,ref="4")
res <- results(deseq,contrast = c("deseq_groups", "5", "4"))
resOrdered <- res[order(res$padj),]
resOrdered <- resOrdered[complete.cases(resOrdered),]
resOrdered <- data.frame(resOrdered)
resOrdered <- left_join(rownames_to_column(resOrdered,var = "ensgene"), grch38, by = "ensgene") %>% select(symbol,log2FoldChange,padj) 

res_for_table <- resOrdered %>% dplyr::filter(padj < 0.1)
datatable(res_for_table, extensions = 'Buttons', options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))

2.6.6 GSEA CD19+ vs CD19+GFP+

plot_gsea(resOrdered)

LS0tCnRpdGxlOiAiRXhwbG9yaW5nIE1MTCBTYW1wbGVzIgphdXRob3I6IDxhIGhyZWY9Imh0dHBzOi8vY2hlbGEtamFtZXMuZ2l0aHViLmlvLyI+IDxoMz4gQ2hlbGEgSmFtZXMgPC9oMz4gPC9hPiBcbmV3bGluZSBDYW5jZXIgSW5zdGl0dXRlLCBVQ0wsIFVLCmRhdGU6IDE1IEFwcmwgMjAxOQpvdXRwdXQ6CiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBlbmNvZGluZzogIlVURi04IgplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KaHRtbHRvb2xzOjp0YWdMaXN0KHJtYXJrZG93bjo6aHRtbF9kZXBlbmRlbmN5X2ZvbnRfYXdlc29tZSgpKQprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbCA9IFRSVUUsIGNhY2hlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQpgYGAKCgpgYGB7ciwgIG91dC53aWR0aCA9ICIxMDAlIiwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdmEpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocHJlcHJvY2Vzc0NvcmUpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShCaW9iYXNlKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShzY2F0ZXIpCmxpYnJhcnkoZ2doaWdobGlnaHQpCmxpYnJhcnkoYW5ub3RhYmxlcykKbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkoRFQpCmxpYnJhcnkocHVycnIpCmBgYAoKIyBJbnZlc3RpZ2F0aW5nIE1MTCBzYW1wbGVzCgojIFRyYW5zY3JpcHRpb25hbCBwcm9maWxlIFBDQSBhbmQgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiAoREdBKQoKIyMgU29ydGluZwoKSXNvbGF0ZWQgY2VsbHMgd2VyZSBzb3J0ZWQgdXNpbmcgYSB2YXJpZXR5IG9mIGNlbGwgbWFya2VycwoKIyMgUk5BLXNlcQoKUk5BIHdhcyBpc29sYXRlZCBhbmQgc2VxdWVuY2VkIGZyb20gdGhlIHZhcmlvdXMgY2VsbCB0eXBlcy4KCiMjIFNhbXBsZSBzdW1tYXJ5CgpgYGB7cn0Kc2FtcGxlX3RhYmxlIDwtIHJlYWQuZGVsaW0oJ3NhbXBsZS50YWJsZScsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCnJvd25hbWVzKHNhbXBsZV90YWJsZSkgPC0gc2FtcGxlX3RhYmxlJE5leHRlcmFfWFRfaW5kZXgKCnNhbXBsZV90YWJsZSROZXh0ZXJhX1hUX2luZGV4IDwtIE5VTEwKc2FtcGxlX3RhYmxlJFJlcGxpY2F0ZXMuIDwtIE5VTEwKc2FtcGxlX3RhYmxlJGRlc2VxX2dyb3VwcyA8LSBjKCIxIiwiNiIsIjMiLCIyIiwiNCIsIjEiLCIzIiwiMiIsIjUiLCIxIiwiNSIsIjIiLCI1IiwiMSIsIjMiLCI2IiwiMiIsIjYiLCIzIikKZGF0YXRhYmxlKHNhbXBsZV90YWJsZSkgJT4lIAogIGZvcm1hdFN0eWxlKCJQaGVub3R5cGUiLCBiYWNrZ3JvdW5kQ29sb3IgPSBzdHlsZUVxdWFsKGMoIkNEMzMrIiwiQ0QzMytHRlAtIiwiQ0QzMytHRlArIiwiQ0QxOSsiLCJDRDE5K0dGUCsiKSwgYygiI0VBRDNCRiIsIiNBQTk0ODYiLCAiI0I2ODU0RCIsIiM5OTg2QTUiLCIjNzk0MDJFIikpKQpgYGAKCiMjIFBDQSAKCiMjIyBQQ0Egd29ya2luZyB3aXRoIEZQS01TIAoKYGBge3J9CiMgZnBrbV9kYXRhIDwtIHB1cnJyOjptYXAocGFzdGUwKCJteXJpYWQvIixyb3duYW1lcyhzYW1wbGVfdGFibGUpLCIvcmVwbGljYXRlXzEvc3RyaW5ndGllLyIscm93bmFtZXMoc2FtcGxlX3RhYmxlKSwiLmdlbmVfYWJ1bmQudGFiIiksIHJlYWQudGFibGUsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsIHNlcD0iXHQiLCBoZWFkZXIgPSBUUlVFKSAlPiUgcmVkdWNlKGxlZnRfam9pbiwgYnkgPSAiR2VuZS5JRCIpICU+JSBzZWxlY3QoIkdlbmUuSUQiLCJHZW5lLk5hbWUiLGNvbnRhaW5zKCJGUEtNIikpCiMgCiMgY29sbmFtZXMoZnBrbV9kYXRhKSA8LSBjKCJHZW5lLklEIiwiR2VuZS5OYW1lIixyb3duYW1lcyhzYW1wbGVfdGFibGUpKQojIHNhdmVSRFMoZnBrbV9kYXRhLCJmcGttX2RhdGEucmRzIikKZnBrbV9kYXRhIDwtIHJlYWRSRFMoImZwa21fZGF0YS5yZHMiKQojIyBUUE0gZGF0YQojIHRwbV9kYXRhIDwtIHB1cnJyOjptYXAocGFzdGUwKCJteXJpYWQvIixyb3duYW1lcyhzYW1wbGVfdGFibGUpLCIvcmVwbGljYXRlXzEvc3RyaW5ndGllLyIscm93bmFtZXMoc2FtcGxlX3RhYmxlKSwiLmdlbmVfYWJ1bmQudGFiIiksIHJlYWQudGFibGUsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsIHNlcD0iXHQiLCBoZWFkZXIgPSBUUlVFKSAlPiUgcmVkdWNlKGxlZnRfam9pbiwgYnkgPSAiR2VuZS5JRCIpICU+JSBzZWxlY3QoIkdlbmUuSUQiLCJHZW5lLk5hbWUiLGNvbnRhaW5zKCJUUE0iKSkKIyAKIyBjb2xuYW1lcyh0cG1fZGF0YSkgPC0gYygiR2VuZS5JRCIsIkdlbmUuTmFtZSIscm93bmFtZXMoc2FtcGxlX3RhYmxlKSkKIyBzYXZlUkRTKHRwbV9kYXRhICwgInRwbV9kYXRhLnJkcyIpCgp0cG1fZGF0YSA8LSByZWFkUkRTKCJ0cG1fZGF0YS5yZHMiKQoKIyMgRmlsdGVyIGdlbmVzIHdpdGggYSBsb3cgZXhwcmVzc2lvbiB0aHJvdWdob3V0CiMgLSBzZWxlY3Qgcm93cyB3aXRoIG1heCB2YWx1ZSA+PSAyCmZwa21fZGF0YSA8LSBmcGttX2RhdGFbYXBwbHkoZnBrbV9kYXRhWywzOjIxXSwgMSwgbWF4KSA+PSAyLF0KCmZwa21zX2NvdW50c19vbmx5IDwtIGZwa21fZGF0YVssMzoyMV0KIyMgTG9nIHRyYW5zZm9ybSB0aGUgZGF0YQpsb2cuZGF0YSA9IGxvZzIoZnBrbXNfY291bnRzX29ubHkrMSkKCmNvbG5hbWVzKGxvZy5kYXRhKSA8LSBjb2xuYW1lcyhmcGttX2RhdGFbLDM6MjFdKQpyb3duYW1lcyhsb2cuZGF0YSkgPC0gbWFrZS5uYW1lcyhmcGttX2RhdGEkR2VuZS5OYW1lLHVuaXF1ZSA9IFRSVUUpCiMjIFRyYW5zcG9zZSB0aGUgbWF0cml4IGJhY2sgZm9yIFBDQQp0X2xvZy5kYXRhID0gdChsb2cuZGF0YSkKCmZpdCA8LSBwcmNvbXAodF9sb2cuZGF0YSkKCng9IlBDMSIKeT0iUEMyIgp4LmxhYiA9IHBhc3RlKHgsIHNwcmludGYoJyglMC4xZiUlIGV4cGxhaW5lZCB2YXIuKScsIDEwMCAqIGZpdCRzZGV2W3doaWNoKGNvbG5hbWVzKGZpdCR4KSA9PSB4KV1eMi9zdW0oZml0JHNkZXZeMikpKQp5LmxhYiA9IHBhc3RlKHksIHNwcmludGYoJyglMC4xZiUlIGV4cGxhaW5lZCB2YXIuKScsIDEwMCAqIGZpdCRzZGV2W3doaWNoKGNvbG5hbWVzKGZpdCR4KSA9PSB5KV1eMi9zdW0oZml0JHNkZXZeMikpKQpkYXRhIDwtIGRhdGEuZnJhbWUoZml0JHgpCmRhdGE8LXJvd25hbWVzX3RvX2NvbHVtbihkYXRhLCB2YXIgPSAic2FtcGxlIikKZGF0YTwtbGVmdF9qb2luKGRhdGEscm93bmFtZXNfdG9fY29sdW1uKHNhbXBsZV90YWJsZSwgdmFyID0gInNhbXBsZV9pZCIpLCBieSA9IGMoInNhbXBsZSIgPSAic2FtcGxlX2lkIikpCgpnZ3Bsb3QoZGF0YSxhZXMoUEMxLFBDMiwgY29sb3VyID0gUGhlbm90eXBlICkpICsgZ2VvbV9wb2ludChzaXplID0gMyApICsgCiAgZ2VvbV9sYWJlbF9yZXBlbChhZXMobGFiZWwgPSBTYW1wbGVzKSwgc2l6ZSA9IDIsYm94LnBhZGRpbmcgICA9IDAuMSxwb2ludC5wYWRkaW5nID0gMC4xLAogICAgICAgICAgICAgICAgICAgc2VnbWVudC5jb2xvciA9ICdncmV5NTAnKSArIHRoZW1lX21pbmltYWwoKSArIHhsYWIoeC5sYWIpICsgeWxhYih5LmxhYikgKwogICBnZ3RpdGxlKGxhYmVsID0gIkZQS00gUENBOyBubyBxdWFudGlsZSBub3JtYWxpemF0aW9uIikKCgpgYGAKCgojIyMgUENBIGFuZCBbcXVhbnRpbGUgbm9ybWFsaXphdGlvbl0oaHR0cDovL2dlbm9taWNzY2xhc3MuZ2l0aHViLmlvL2Jvb2svcGFnZXMvbm9ybWFsaXphdGlvbi5odG1sKQoKYGBge3J9CgojIyBRdWFudGlsZSBub3JtYWxpemF0aW9uCm5vcm1hbGl6ZWQuZGF0YSA9IG5vcm1hbGl6ZS5xdWFudGlsZXMoYXMubWF0cml4KGxvZy5kYXRhKSkgIyBBIHF1YW50aWxlIG5vcm1hbGl6YXRpb24uCgojIyBUcmFuc3Bvc2UgdGhlIG1hdHJpeCBiYWNrIGZvciBQQ0EKbm9ybWFsaXplZC5kYXRhID0gdChub3JtYWxpemVkLmRhdGEpCgojIyByZS1hdHRhY2ggcm93bmFtZXMgY29sbmFtZXMKcm93bmFtZXMobm9ybWFsaXplZC5kYXRhKSA8LSBjb2xuYW1lcyhsb2cuZGF0YSkKY29sbmFtZXMobm9ybWFsaXplZC5kYXRhKSA8LSByb3duYW1lcyhsb2cuZGF0YSkKCmZpdCA8LSBwcmNvbXAobm9ybWFsaXplZC5kYXRhKQoKeC5sYWIgPSBwYXN0ZSh4LCBzcHJpbnRmKCcoJTAuMWYlJSBleHBsYWluZWQgdmFyLiknLCAxMDAgKiBmaXQkc2Rldlt3aGljaChjb2xuYW1lcyhmaXQkeCkgPT0geCldXjIvc3VtKGZpdCRzZGV2XjIpKSkKeS5sYWIgPSBwYXN0ZSh5LCBzcHJpbnRmKCcoJTAuMWYlJSBleHBsYWluZWQgdmFyLiknLCAxMDAgKiBmaXQkc2Rldlt3aGljaChjb2xuYW1lcyhmaXQkeCkgPT0geSldXjIvc3VtKGZpdCRzZGV2XjIpKSkKZGF0YSA8LSBkYXRhLmZyYW1lKGZpdCR4KQpkYXRhPC1yb3duYW1lc190b19jb2x1bW4oZGF0YSwgdmFyID0gInNhbXBsZSIpCmRhdGE8LWxlZnRfam9pbihkYXRhLHJvd25hbWVzX3RvX2NvbHVtbihzYW1wbGVfdGFibGUsIHZhciA9ICJzYW1wbGVfaWQiKSwgYnkgPSBjKCJzYW1wbGUiID0gInNhbXBsZV9pZCIpKQoKZ2dwbG90KGRhdGEsYWVzKFBDMSxQQzIsIGNvbG91ciA9IFBoZW5vdHlwZSApKSArIGdlb21fcG9pbnQoc2l6ZSA9IDMgKSArIAogIGdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsID0gU2FtcGxlcyksIHNpemUgPSAyLGJveC5wYWRkaW5nICAgPSAwLjEscG9pbnQucGFkZGluZyA9IDAuMSwKICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuY29sb3IgPSAnZ3JleTUwJykgKyB0aGVtZV9taW5pbWFsKCkgKyB4bGFiKHgubGFiKSArIHlsYWIoeS5sYWIpICsKICBnZ3RpdGxlKGxhYmVsID0gIkZQS00gUENBOyB3aXRoIHF1YW50aWxlIG5vcm1hbGl6YXRpb24iKQoKYGBgCgojIyMgVFBNcyB3aXRob3V0IG5vcm1hbGl6YXRpb24KCmBgYHtyfQojIyBGaWx0ZXIgZ2VuZXMgd2l0aCBhIGxvdyBleHByZXNzaW9uIHRocm91Z2hvdXQKIyAtIHNlbGVjdCByb3dzIHdpdGggbWF4IHZhbHVlID49IDIKdHBtX2RhdGEgPC0gdHBtX2RhdGFbYXBwbHkodHBtX2RhdGFbLDM6MjFdLCAxLCBtYXgpID49IDIsXQoKdHBtX2NvdW50c19vbmx5IDwtIHRwbV9kYXRhWywzOjIxXQojIyBMb2cgdHJhbnNmb3JtIHRoZSBkYXRhCmxvZy5kYXRhID0gbG9nMih0cG1fY291bnRzX29ubHkrMSkKCmNvbG5hbWVzKGxvZy5kYXRhKSA8LSBjb2xuYW1lcyh0cG1fZGF0YVssMzoyMV0pCnJvd25hbWVzKGxvZy5kYXRhKSA8LSBtYWtlLm5hbWVzKHRwbV9kYXRhJEdlbmUuTmFtZSx1bmlxdWUgPSBUUlVFKQojIyBUcmFuc3Bvc2UgdGhlIG1hdHJpeCBiYWNrIGZvciBQQ0EKdF9sb2cuZGF0YSA9IHQobG9nLmRhdGEpCgpmaXQgPC0gcHJjb21wKHRfbG9nLmRhdGEpCgp4PSJQQzEiCnk9IlBDMiIKeC5sYWIgPSBwYXN0ZSh4LCBzcHJpbnRmKCcoJTAuMWYlJSBleHBsYWluZWQgdmFyLiknLCAxMDAgKiBmaXQkc2Rldlt3aGljaChjb2xuYW1lcyhmaXQkeCkgPT0geCldXjIvc3VtKGZpdCRzZGV2XjIpKSkKeS5sYWIgPSBwYXN0ZSh5LCBzcHJpbnRmKCcoJTAuMWYlJSBleHBsYWluZWQgdmFyLiknLCAxMDAgKiBmaXQkc2Rldlt3aGljaChjb2xuYW1lcyhmaXQkeCkgPT0geSldXjIvc3VtKGZpdCRzZGV2XjIpKSkKZGF0YSA8LSBkYXRhLmZyYW1lKGZpdCR4KQpkYXRhPC1yb3duYW1lc190b19jb2x1bW4oZGF0YSwgdmFyID0gInNhbXBsZSIpCmRhdGE8LWxlZnRfam9pbihkYXRhLHJvd25hbWVzX3RvX2NvbHVtbihzYW1wbGVfdGFibGUsIHZhciA9ICJzYW1wbGVfaWQiKSwgYnkgPSBjKCJzYW1wbGUiID0gInNhbXBsZV9pZCIpKQoKZ2dwbG90KGRhdGEsYWVzKFBDMSxQQzIsIGNvbG91ciA9IFBoZW5vdHlwZSApKSArIGdlb21fcG9pbnQoc2l6ZSA9IDMgKSArIAogIGdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsID0gU2FtcGxlcyksIHNpemUgPSAyLGJveC5wYWRkaW5nICAgPSAwLjEscG9pbnQucGFkZGluZyA9IDAuMSwKICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuY29sb3IgPSAnZ3JleTUwJykgKyB0aGVtZV9taW5pbWFsKCkgKyB4bGFiKHgubGFiKSArIHlsYWIoeS5sYWIpICsKICAgZ2d0aXRsZShsYWJlbCA9ICJUUE0gUENBOyBubyBxdWFudGlsZSBub3JtYWxpemF0aW9uIikKYGBgCgojI1BDQSB3aXRoIGNvdW50cwojIyNVc2luZyBbc2NhdGVyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvc2NhdGVyLmh0bWwpIG5vcm1hbGl6YXRpb24gCgpgYGB7cn0KIyBiaWdfY291bnRzIDwtIHBhc3RlMCgibXlyaWFkLyIscm93bmFtZXMoc2FtcGxlX3RhYmxlKSwiL3JlcGxpY2F0ZV8xL3FjLyIscm93bmFtZXMoc2FtcGxlX3RhYmxlKSwgIl9RQy5zdW1tYXJ5LnR4dC9RQy5nZW5lQ291bnRzLmZvcm1hdHRlZC5mb3IuREVTZXEudHh0Lmd6IikgJT4lIG1hcChnemZpbGUpICU+JSBtYXAocmVhZC50YWJsZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLCBzZXAgPSAiXHQiKSAgJT4lIHJlZHVjZShsZWZ0X2pvaW4sIGJ5ID0gIlYxIikgCiMgCiMgY29sbmFtZXMoYmlnX2NvdW50cykgPC0gYygiZW5zZ2VuZSIsIHJvd25hbWVzKHNhbXBsZV90YWJsZSkpCiMgcm93bmFtZXMoYmlnX2NvdW50cykgPC0gYmlnX2NvdW50cyRlbnNnZW5lCiMgYmlnX2NvdW50cyRlbnNnZW5lIDwtIE5VTEwKIyBiaWdfY291bnRzIDwtIGJpZ19jb3VudHNbIWlzLm5hKHJvd1N1bXMoYmlnX2NvdW50cykpLF0KIyBzYXZlUkRTKGJpZ19jb3VudHMsImJpZ19jb3VudHMucmRzIikKYmlnX2NvdW50cyA8LSByZWFkUkRTKCJiaWdfY291bnRzLnJkcyIpCmNvdW50X21hdHJpeCA8LSBhcy5tYXRyaXgoYmlnX2NvdW50cykKY29sbmFtZXMoY291bnRfbWF0cml4KSA8LSByb3duYW1lcyhzYW1wbGVfdGFibGUpCnJvd25hbWVzKGNvdW50X21hdHJpeCkgPC0gcm93bmFtZXMoYmlnX2NvdW50cykKCnNjZSA8LSBTaW5nbGVDZWxsRXhwZXJpbWVudChsaXN0KGNvdW50cyA9IGNvdW50X21hdHJpeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gc2FtcGxlX3RhYmxlKQpzY2UgPC0gc2NlW3Jvd1N1bXMoY291bnRzKHNjZSkpID4gIDAsXQpzY2UgPC0gY2FsY3VsYXRlUUNNZXRyaWNzKHNjZSkKCmBgYAoKYGBge3J9CiMjIHByZXBhcmUgdG90YWwgY291bnQgYW5kIHRvdGFsIGZlYXR1cmVzIGRhdGEKCm15X2RmIDwtIGRhdGEuZnJhbWUoInNhbXBsZV9pZCIgPSBjb2xuYW1lcyhzY2UpLCAidG90YWxfY291bnRzIiA9IHNjZSR0b3RhbF9jb3VudHMsCiAgICAgICAgICAgICAgICAgICAgInRvdGFsX2ZlYXR1cmVzIiA9IHNjZSR0b3RhbF9mZWF0dXJlc19ieV9jb3VudHMgKQpnZ3Bsb3QobXlfZGYsYWVzKHRvdGFsX2NvdW50cykpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwKSArIAogIHRoZW1lX21pbmltYWwoKSArIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiB0b3RhbCBjb3VudHMiKSArIHlsYWIoIm51bWJlciBvZiBzYW1wbGVzIikgKyB4bGFiKCJ0b3RhbCBjb3VudHMiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QobXlfZGYsYWVzKHRvdGFsX2ZlYXR1cmVzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTApICsgCiAgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIG9mIHRvdGFsIGZlYXR1cmVzIikgKyB5bGFiKCJudW1iZXIgb2Ygc2FtcGxlcyIpICsgeGxhYigidG90YWwgZmVhdHVyZXMiKQoKYGBgCgojIyNQQ0Egd2l0aG91dCBiYXRjaCBlZmZlY3QgcmVtb3ZhbAoKYGBge3J9CnNpemVGYWN0b3JzKHNjZSkgPC0gbGlicmFyeVNpemVGYWN0b3JzKHNjZSkKc2NlIDwtIG5vcm1hbGl6ZShzY2UpCgpzY2UgPC0gcnVuUENBKHNjZSkKCmRhdGEgPC0gcmVkdWNlZERpbShzY2UpCnBlcmNlbnRfdmFyX1BDMTwtIHBhc3RlKCJQQzEiLCBzcHJpbnRmKCcoJTAuMWYlJSBleHBsYWluZWQgdmFyLiknLCBhdHRyKGRhdGEsICdwZXJjZW50VmFyJylbMV0gKiAxMDAgKSkKcGVyY2VudF92YXJfUEMyPC0gcGFzdGUoIlBDMiIsIHNwcmludGYoJyglMC4xZiUlIGV4cGxhaW5lZCB2YXIuKScsIGF0dHIoZGF0YSwgJ3BlcmNlbnRWYXInKVsyXSAqIDEwMCApKQoKZGF0YSA8LSBkYXRhLmZyYW1lKGRhdGEpCmRhdGE8LWxlZnRfam9pbihyb3duYW1lc190b19jb2x1bW4oZGF0YSksIHJvd25hbWVzX3RvX2NvbHVtbihzYW1wbGVfdGFibGUpKQoKZ2dwbG90KGRhdGEsYWVzKFBDMSxQQzIsY29sb3VyID0gUGhlbm90eXBlKSkgKyBnZW9tX3BvaW50KHNpemUgPSAzKSArIAogIGdlb21fbGFiZWxfcmVwZWwobGFiZWwgPSBkYXRhJFNhbXBsZXMsIHNpemUgPSAyLGJveC5wYWRkaW5nICAgPSAwLjEscG9pbnQucGFkZGluZyA9IDAuMSwKICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuY29sb3IgPSAnZ3JleTUwJykgKyB0aGVtZV9taW5pbWFsKCkgKyB4bGFiKHBlcmNlbnRfdmFyX1BDMSkgKyB5bGFiKHBlcmNlbnRfdmFyX1BDMikKCmBgYAoKCiMjIERFc2VxMgoKYGBge3J9CmJpZ19jb3VudHMgPC0gYmlnX2NvdW50c1stYyg1ODczNjo1ODczOSksXQoKZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoYmlnX2NvdW50cywgY29sRGF0YSA9IHNhbXBsZV90YWJsZSwgZGVzaWduID0gfmRlc2VxX2dyb3VwcykKbXlfdnN0IDwtIHZzdChkZHMpCgpwY2FEYXRhIDwtIERFU2VxMjo6cGxvdFBDQShteV92c3QsIGludGdyb3VwID1jKCJQaGVub3R5cGUiLCJTYW1wbGVzIikscmV0dXJuRGF0YSA9IFRSVUUpCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoKCmdncGxvdChwY2FEYXRhLGFlcyhQQzEsUEMyLGNvbG91ciA9IFBoZW5vdHlwZSkpICsgZ2VvbV9wb2ludChzaXplID0gMyApICsKICAgICAgICB4bGFiKHBhc3RlMCgiUEMxOiAiLHBlcmNlbnRWYXJbMV0sIiUgdmFyaWFuY2UiKSkgKwogICAgICAgIHlsYWIocGFzdGUwKCJQQzI6ICIscGVyY2VudFZhclsyXSwiJSB2YXJpYW5jZSIpKSArIAogICAgICAgICAgZ2VvbV9sYWJlbF9yZXBlbChhZXMobGFiZWwgPSBwY2FEYXRhJFNhbXBsZXMpLCBzaXplID0gMixib3gucGFkZGluZyAgID0gMC4xLHBvaW50LnBhZGRpbmcgPSAwLjEsCiAgICAgICAgICAgICAgICAgICBzZWdtZW50LmNvbG9yID0gJ2dyZXk1MCcpICsKICAgICAgICB0aGVtZV9taW5pbWFsKCkKCmBgYAoKIyMjIEdlbmVzIGRyaXZpbmcgUEMxIGFuZCBQQzIKCmBgYHtyfQoKdG9wX2NvbnRyaWJzID0gZnVuY3Rpb24ob2JqZWN0KSB7CiAgIyBjYWxjdWxhdGUgdGhlIHZhcmlhbmNlIGZvciBlYWNoIGdlbmUKICBydiA8LSByb3dWYXJzKGFzc2F5KG9iamVjdCkpCgogICMgc2VsZWN0IHRoZSAxMDAwIHRvcCBnZW5lcyBieSB2YXJpYW5jZQogIHNlbGVjdCA8LSBvcmRlcihydiwgZGVjcmVhc2luZz1UUlVFKVtzZXFfbGVuKG1pbigxMDAwLCBsZW5ndGgocnYpKSldCgogICMgcGVyZm9ybSBhIFBDQSBvbiB0aGUgZGF0YSBpbiBhc3NheSh4KSBmb3IgdGhlIHNlbGVjdGVkIGdlbmVzCiAgcGNhIDwtIHByY29tcCh0KGFzc2F5KG9iamVjdClbc2VsZWN0LF0pKQoKICAjIHRoZSBjb250cmlidXRpb24gdG8gdGhlIHRvdGFsIHZhcmlhbmNlIGZvciBlYWNoIGNvbXBvbmVudAogIHBlcmNlbnRWYXIgPC0gcGNhJHNkZXZeMiAvIHN1bSggcGNhJHNkZXZeMiApCgogICMgVG9wIDIwIGNvbnRyaWJ1dGVycyB0byBQQzEgUEMyCiAgUENBMV9jb250cmliIDwtIHNvcnQoYWJzKHBjYSRyb3RhdGlvblssMV0pLCBkZWNyZWFzaW5nID0gVFJVRSApWzE6MjBdCiAgUENBMl9jb250cmliIDwtIHNvcnQoYWJzKHBjYSRyb3RhdGlvblssMl0pLCBkZWNyZWFzaW5nID0gVFJVRSApWzE6MjBdCiAgUENBX2NvbnRyaWIgPC0gYyhQQ0ExX2NvbnRyaWIsIFBDQTJfY29udHJpYikKICBQQ0FfY29udHJpYiA8LSBkYXRhLmZyYW1lKCJlbnNnZW5lIiA9IG5hbWVzKFBDQV9jb250cmliKSwgUENBX2NvbnRyaWIpCgoKCiAgUENBX2NvbnRyaWIgPC0gY2JpbmQoUENBX2NvbnRyaWIsIGRhdGEuZnJhbWUoIlBDIiA9IGMocmVwKCJQQzEiLDIwKSxyZXAoIlBDMiIsIjIwIikpKSkKICBQQ0FfY29udHJpYiA8LSBsZWZ0X2pvaW4oUENBX2NvbnRyaWIsIGdyY2gzOCwgYnkgPSAiZW5zZ2VuZSIpICU+JSBkcGx5cjo6c2VsZWN0KCJQQyIsInN5bWJvbCIsICJiaW90eXBlIikKICByZXR1cm4oUENBX2NvbnRyaWIpCn0KCmRhdGF0YWJsZSh0b3BfY29udHJpYnMobXlfdnN0KSkKCmBgYAoKIyMgRGlmZmVyZW50aWFsIEdlbmUgRXhwcmVzc2lvbgoKIyMjIENEMzNeK14gdnMgQ0QzM14rXkdGUF4rXgoKYGBge3J9CmRkcyRkZXNlcV9ncm91cHMgPC0gcmVsZXZlbChkZHMkZGVzZXFfZ3JvdXBzLHJlZiA9ICIyIikKCmRlc2VxIDwtIERFU2VxKGRkcykKCnJlcyA8LSByZXN1bHRzKGRlc2VxLGNvbnRyYXN0ID0gYygiZGVzZXFfZ3JvdXBzIiwgIjEiLCAiMiIpKQpyZXNPcmRlcmVkIDwtIHJlc1tvcmRlcihyZXMkcGFkaiksXQpyZXNPcmRlcmVkIDwtIHJlc09yZGVyZWRbY29tcGxldGUuY2FzZXMocmVzT3JkZXJlZCksXQpyZXNPcmRlcmVkIDwtIGRhdGEuZnJhbWUocmVzT3JkZXJlZCkKcmVzT3JkZXJlZCA8LSBsZWZ0X2pvaW4ocm93bmFtZXNfdG9fY29sdW1uKHJlc09yZGVyZWQsdmFyID0gImVuc2dlbmUiKSwgZ3JjaDM4LCBieSA9ICJlbnNnZW5lIikgJT4lIHNlbGVjdChzeW1ib2wsbG9nMkZvbGRDaGFuZ2UscGFkaikKCnJlc19mb3JfdGFibGUgPC0gcmVzT3JkZXJlZCAgJT4lIGRwbHlyOjpmaWx0ZXIocGFkaiA8IDAuMSkKZGF0YXRhYmxlKHJlc19mb3JfdGFibGUsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKCmBgYAoKCiMjIyBHU0VBIENEMzNeK14gdnMgQ0QzM14rXkdGUF4rXgoKYGBge3J9CgpoYWxsbWFyayA8LSBnbXRQYXRod2F5cygifi9nZW5vbWVfYXBwcy9HU0VBL2guYWxsLnY2LjIuc3ltYm9scy5nbXQiKQoKcGxvdF9nc2VhIDwtIGZ1bmN0aW9uKHJlcyl7CnJlcyA8LSByZXNbY29tcGxldGUuY2FzZXMocmVzKSxdCnJlcyRmY1NpZ24gPC0gc2lnbihyZXMkbG9nMkZvbGRDaGFuZ2UpCnJlcyRsb2dQIDwtIC1sb2cxMChyZXMkcGFkaikKcmVzJG1ldHJpYyA8LSByZXMkbG9nUC9yZXMkZmNTaWduCnk8LXJlc1ssYygic3ltYm9sIiwgIm1ldHJpYyIpXQpnZW5lTGlzdCA8LSB5JG1ldHJpYwpuYW1lcyhnZW5lTGlzdCkgPC0geSRzeW1ib2wKZ2VuZUxpc3QgPC0gZ2VuZUxpc3Rbb3JkZXIoZ2VuZUxpc3QsIGRlY3JlYXNpbmcgPSBUKV0KZmdzZWFSZXMgPC0gZmdzZWEoaGFsbG1hcmssIGdlbmVMaXN0LCBucGVybT0xMDAwMCwgbWF4U2l6ZSA9IDUwMCkKZmdzZWFSZXMgPC0gZmdzZWFSZXNbZmdzZWFSZXMkcGFkaiA8IDAuMDUsXQpwbG90R3NlYVRhYmxlKGhhbGxtYXJrW2Znc2VhUmVzJHBhdGh3YXldLCBnZW5lTGlzdCwgZmdzZWFSZXMsIGdzZWFQYXJhbSA9IDAuNCkKfQoKcGxvdF9nc2VhKHJlc09yZGVyZWQpCmBgYAoKCiMjIyBER0UgQ0QzM14rXkdGUF4tXiB2cyBDRDMzXiteR0ZQXiteCgpgYGB7cn0KcmVzIDwtIHJlc3VsdHMoZGVzZXEsY29udHJhc3QgPSBjKCJkZXNlcV9ncm91cHMiLCAiMyIsICIyIikpCnJlc09yZGVyZWQgPC0gcmVzW29yZGVyKHJlcyRwYWRqKSxdCnJlc09yZGVyZWQgPC0gcmVzT3JkZXJlZFtjb21wbGV0ZS5jYXNlcyhyZXNPcmRlcmVkKSxdCnJlc09yZGVyZWQgPC0gZGF0YS5mcmFtZShyZXNPcmRlcmVkKQpyZXNPcmRlcmVkIDwtIGxlZnRfam9pbihyb3duYW1lc190b19jb2x1bW4ocmVzT3JkZXJlZCx2YXIgPSAiZW5zZ2VuZSIpLCBncmNoMzgsIGJ5ID0gImVuc2dlbmUiKSAlPiUgc2VsZWN0KHN5bWJvbCxsb2cyRm9sZENoYW5nZSxwYWRqKSAKCnJlc19mb3JfdGFibGUgPC0gcmVzT3JkZXJlZCAlPiUgZHBseXI6OmZpbHRlcihwYWRqIDwgMC4xKQpkYXRhdGFibGUocmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJykpKQoKCmBgYAoKIyMjIEdTRUEgQ0QzM14rXkdGUF4tXiB2cyBDRDMzXiteR0ZQXiteCgpgYGB7cn0KcGxvdF9nc2VhKHJlc09yZGVyZWQpCmBgYAoKCgojIyMgREdFIENEMTleK14gdnMgQ0QxOV4rXkdGUF4rXgoKYGBge3J9CmRkcyRkZXNlcV9ncm91cHMgPC0gcmVsZXZlbChkZHMkZGVzZXFfZ3JvdXBzLHJlZj0iNCIpCnJlcyA8LSByZXN1bHRzKGRlc2VxLGNvbnRyYXN0ID0gYygiZGVzZXFfZ3JvdXBzIiwgIjUiLCAiNCIpKQpyZXNPcmRlcmVkIDwtIHJlc1tvcmRlcihyZXMkcGFkaiksXQpyZXNPcmRlcmVkIDwtIHJlc09yZGVyZWRbY29tcGxldGUuY2FzZXMocmVzT3JkZXJlZCksXQpyZXNPcmRlcmVkIDwtIGRhdGEuZnJhbWUocmVzT3JkZXJlZCkKcmVzT3JkZXJlZCA8LSBsZWZ0X2pvaW4ocm93bmFtZXNfdG9fY29sdW1uKHJlc09yZGVyZWQsdmFyID0gImVuc2dlbmUiKSwgZ3JjaDM4LCBieSA9ICJlbnNnZW5lIikgJT4lIHNlbGVjdChzeW1ib2wsbG9nMkZvbGRDaGFuZ2UscGFkaikgCgpyZXNfZm9yX3RhYmxlIDwtIHJlc09yZGVyZWQgJT4lIGRwbHlyOjpmaWx0ZXIocGFkaiA8IDAuMSkKZGF0YXRhYmxlKHJlc19mb3JfdGFibGUsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKCgpgYGAKCiMjIyBHU0VBIENEMTleK14gdnMgQ0QxOV4rXkdGUF4rXgoKYGBge3J9CnBsb3RfZ3NlYShyZXNPcmRlcmVkKQpgYGAKCg==